package net.halflite.mzkr.interceptor;

import java.util.Arrays;
import java.util.Collection;
import java.util.Map;

import javassist.Modifier;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import net.halflite.mzkr.annotation.Login;
import net.halflite.mzkr.annotation.SessionRequired;
import net.halflite.mzkr.dto.user.UserDto;
import net.halflite.mzkr.type.LoginType;
import net.halflite.mzkr.type.UserStatusType;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.impl.NestedMethodInvocation;
import org.seasar.framework.aop.interceptors.AbstractInterceptor;

public class AuthActionInterceptor extends AbstractInterceptor {

	private static final long serialVersionUID = 1L;

	@Resource
	private Map<String, Object> requestScope;

	@Resource
	private Map<String, Object> sessionScope;

	@Resource
	private HttpServletResponse response;

	/** 空文字 */
	private static final String EMPTY = "";
	
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// publicでないメソッドは即リターン
		if (!Modifier.isPublic(invocation.getMethod().getModifiers())) return invocation.proceed();
		
		String loginPath = getLoginRequiredPath(invocation);
		if (loginPath == null || !loginPath.isEmpty()) return loginPath;
		
		String redirectPath = getSessionRequiredPath(invocation);
		if (!redirectPath.isEmpty()) return redirectPath;
		
		return invocation.proceed();
	}
	
	/** 
	 * 未ログイン時に戻すパス情報
	 * 
	 * @param invocation
	 * @return 文字列: 戻りパス情報(ログイン用)<br>
	 * null: ステータスを設定済み<br>
	 * 空文字: ログインが必要ない、もしくはログインが済んでいる時
	 * 
	 */
	private String getLoginRequiredPath(MethodInvocation invocation) {
		NestedMethodInvocation nested = (NestedMethodInvocation) invocation;
		Login classAnnotation = nested.getTargetClass().<Login>getAnnotation(Login.class);
		Login methodAnnotation = nested.getMethod().getAnnotation(Login.class);
		if (classAnnotation == null && methodAnnotation == null) return EMPTY;
		
		UserDto user = (UserDto) sessionScope.get(LoginType.USER.getKey());
		if (user == null) {
			String backPath = (String) requestScope.get("javax.servlet.forward.servlet_path");
			sessionScope.put("backPath", backPath);
			return "/login/?redirect=true";
		}
		LoginType type = (methodAnnotation != null) ? methodAnnotation.value() :
			(classAnnotation != null)  ? classAnnotation.value() : LoginType.USER ;
		if (type == LoginType.ADMIN  && user.status != UserStatusType.ADMIN) {
			response.setStatus(403);
			return null;
		}
		
		return EMPTY;
	}
	
	/** 
	 * 指定されたセッション情報が無い時に戻すパス情報
	 * 
	 * @param invocation
	 * @return 文字列: 戻りパス情報(ログイン用)<br>
	 * 空文字: アノテーション未設定、もしくはセッション情報が全て存在する場合
	 */
	private String getSessionRequiredPath(MethodInvocation invocation) {
		SessionRequired annotation = invocation.getMethod().getAnnotation(SessionRequired.class);
		if (annotation == null) return EMPTY;

		Collection<String> keys = Arrays.asList(annotation.keys());
		return (sessionScope.keySet().containsAll(keys)) ? EMPTY : annotation.redirect() + "?redirect=true";
	}
}
